package com.gjy.scheduling.monitor.processor;

import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.config.ScheduledTask;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Semaphore;

/**
 * @author gjy
 * @version 1.0
 * @since 2025-08-30 20:56:12
 */
public class DsContainer<T extends DsTaskInfo> {

    private static final Logger log = LoggerFactory.getLogger(DsContainer.class);
    /**
     * IDSTaskInfo和真实任务的关联关系
     * <p>
     * <task_id, <Task, <Scheduled, Semaphore>>>
     * task_id：作为主键，确保一个 IDSTaskInfo 只会被注册进一次  本程序用kpi_code标识唯一性
     * T：暂存当初注册时的 IDSTaskInfo，用于跟最新的 IDSTaskInfo 比较参数是否发生变化
     * ScheduledTask：暂存当初注册时生成的任务，如果需要取消任务的话，需要拿到该对象
     * Semaphore：确保每个任务实际执行时只有一个线程执行，不会产生并发问题
     */
    private final Map<String, Pair<T, Pair<ScheduledTask, Semaphore>>> scheduleMap = new ConcurrentHashMap<>();

    private final ScheduledTaskRegistrar taskRegistrar;

    public DsContainer(ScheduledTaskRegistrar taskRegistrar) {
        this.taskRegistrar = taskRegistrar;
    }

    /**
     * 动态任务管理
     * scheduleMap用于保存正在运行的任务实例。每次检查当前扫描到的任务是否正在运行，如果存在，则检查任务是否需要更新。反之直接注册定时任务
     * 将scheduleMap中旧任务对象和当前任务对象进行比对，如果存在差异，则先取消任务，再重新注册定时任务。如果当前任务状态失效，则直接销毁定时任务
     *
     * @param taskInfo    任务信息
     * @param triggerTask 任务的触发规则
     */
    public void checkTask(T taskInfo, TriggerTask triggerTask) {
        String taskId = taskInfo.getId();
        if (scheduleMap.containsKey(taskId)) {
            if (taskInfo.isValid()) {
                T old = scheduleMap.get(taskId).getLeft();
                if (old.isChange(taskInfo)) {
                    log.info("数据库配置变动重新注册定时任务: {}", taskId);
                    canalTask(taskId);
                    registerTask(taskInfo, triggerTask);
                }
            } else {
                log.info("当前定时任务失效: {}", taskInfo);
                canalTask(taskId);
            }
        } else {
            if (taskInfo.isValid()) {
                log.info("注册定时任务: {}", taskId);
                registerTask(taskInfo, triggerTask);
            }
        }
    }

    public Semaphore getSemaphore(String taskId) {
        return this.scheduleMap.get(taskId).getRight().getRight();
    }

    /**
     * 注册定时任务
     *
     * @param taskInfo:    定时任务信息
     * @param triggerTask: 触发器
     */
    private void registerTask(T taskInfo, TriggerTask triggerTask) {
        try {
            ScheduledTask task = taskRegistrar.scheduleTriggerTask(triggerTask);
            this.scheduleMap.put(taskInfo.getId(), Pair.of(taskInfo, Pair.of(task, new Semaphore(1))));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 销毁定时任务
     *
     * @param taskId: 任务id
     */
    private void canalTask(String taskId) {
        try {
            Pair<T, Pair<ScheduledTask, Semaphore>> pair = scheduleMap.get(taskId);
            if (null != pair) {
                pair.getRight().getLeft().cancel();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
